Strict
#REFLECTION_FILTER=""

Private
Import externs
Import reflection

Private
' A simple reference counting class to ensure that Monkey objects stored in the Lua state will not be GC'd by the Monkey runtime
Class ReferenceCounter
	Field referenceObjects:Object[] = New Object[100]
	Field referenceCounts:Int[] = New Int[100]
	Field references:Int = 0
	
	Method Retain:Void(obj:Object)
		For Local i:Int = 0 Until references
			If referenceObjects[i] = obj Then
				referenceCounts[i] += 1
				Return
			End
		Next
		If references >= referenceObjects.Length Then
			referenceObjects = referenceObjects.Resize(referenceObjects.Length*2)
			referenceCounts = referenceCounts.Resize(referenceCounts.Length*2)
		End
		referenceObjects[references] = obj
		referenceCounts[references] = 1
		references += 1
	End
	
	Method Release:Void(obj:Object)
		For Local i:Int = 0 Until references
			If referenceObjects[i] = obj Then
				referenceCounts[i] -= 1
				If referenceCounts[i] <= 0 Then
					references -= 1
					referenceObjects[i] = referenceObjects[references]
					referenceCounts[i] = referenceCounts[references]
					referenceObjects[references] = Null
					referenceCounts[references] = 0
				End
				Return
			End
		Next	
	End
	
	Method ReleaseAll:Void()
		For Local i:Int = 0 Until referenceObjects.Length
			referenceObjects[i] = Null
			referenceCounts[i] = 0
		Next
		references = 0
	End
End

' Extends ml_MonkeyLua to provide virtual implementations
Class MonkeyLua Extends ml_MonkeyLua
Private
	Field refCounter:ReferenceCounter = New ReferenceCounter
	
	Method ml_GetField:Int(L:lua_State)
		If ml_lua_gettop(L) <> 2 Then
			ml_lua_pushnil(L)
			Return 1
		End
		Local obj:Object = ml_ToMonkeyObject(L,1)
		Local fieldname:String = ml_lua_tostring(L,2)
		Local cinfo:ClassInfo = GetClass(obj)
		If cinfo Then
			Local finfo:FieldInfo = cinfo.GetField(fieldname)
			If finfo Then
				If finfo.Type = IntClass() Then
					ml_lua_pushinteger(L, UnboxInt(finfo.GetValue(obj)))
				ElseIf finfo.Type = FloatClass() Then
					ml_lua_pushnumber(L, UnboxFloat(finfo.GetValue(obj)))
				ElseIf finfo.Type = StringClass() Then
					ml_lua_pushstring(L, UnboxString(finfo.GetValue(obj)))
				ElseIf finfo.Type = BoolClass() Then
					If UnboxBool(finfo.GetValue(obj)) Then
						ml_lua_pushboolean(L, 1)
					Else
						ml_lua_pushboolean(L, 0)
					End
				ElseIf finfo.GetValue(obj) Then
					ml_PushMonkeyObject(L, finfo.GetValue(obj))
				Else
					ml_lua_pushnil(L)
				End
			Else
				' TODO: wrap methods?
				'Local minfo:MethodInfo = cinfo.GetMethod(fieldname)
				ml_lua_pushnil(L)
			End
		Else
			ml_lua_pushnil(L)
		End
		Return 1
	End
	
	Method ml_SetField:Int(L:lua_State)
		If ml_lua_gettop(L) <> 3 Then Return 0
		Local obj:Object = ml_ToMonkeyObject(L,1)
		Local fieldname:String = ml_lua_tostring(L,2)
		Local cinfo:ClassInfo = GetClass(obj)
		Local finfo:FieldInfo = cinfo.GetField(fieldname)
		If finfo Then
			If finfo.Type = IntClass() Then
				finfo.SetValue(obj, BoxInt(ml_lua_tointeger(L, 3)))
			ElseIf finfo.Type = FloatClass() Then
				finfo.SetValue(obj, BoxFloat(ml_lua_tonumber(L, 3)))
			ElseIf finfo.Type = StringClass() Then
				finfo.SetValue(obj, BoxString(ml_lua_tostring(L, 3)))
			ElseIf finfo.Type = BoolClass() Then
				finfo.SetValue(obj, BoxBool(ml_lua_toboolean(L, 3) <> 0))
			ElseIf finfo.GetValue(obj) Then
				finfo.SetValue(obj, ml_ToMonkeyObject(L, 3))
			End
		End
		Return 0
	End
	
	Method ml_Retain:Void(obj:Object)
		refCounter.Retain(obj)
	End
	
	Method ml_Release:Void(obj:Object)
		refCounter.Release(obj)
	End
	
Public
	Method New()
		' This sets the instance in the externed code
		ml_SetMLI(Self)
	End
End

' Private global instance of MonkeyLua
Global MLI:MonkeyLua = New MonkeyLua

Public

' Public global instance of a null pointer (not const!!!)
Global NULL_PTR:Pointer = New Pointer

' lua wrapper functions
' atpanic
Function lua_call:Void(L:lua_State, nargs:Int, nresults:Int) MLI.ml_lua_call(L, nargs, nresults) End
Function lua_checkstack:Int(L:lua_State, extra:Int) Return MLI.ml_lua_checkstack(L, extra) End
Function lua_close:Void(L:lua_State) MLI.ml_lua_close(L) End
Function lua_concat:Void(L:lua_State, n:Int) MLI.ml_lua_concat(L, n) End
' cpcall
Function lua_createtable:Void(L:lua_State, narr:Int, nrec:Int) MLI.ml_lua_createtable(L, narr, nrec) End
' dump
Function lua_equal:Int(L:lua_State, index1:Int, index2:Int) Return MLI.ml_lua_equal(L, index1, index2) End
Function lua_error:Int(L:lua_State) Return MLI.ml_lua_error(L) End
Function lua_gc:Int(L:lua_State, what:Int, data:Int) Return MLI.ml_lua_gc(L, what, data) End
Function lua_getallocf:lua_Alloc(L:lua_State, ud:Pointer) Return MLI.ml_lua_getallocf(L, ud) End
Function lua_getallocf:lua_Alloc(L:lua_State, ud:Pointer, rv:lua_Alloc) Return MLI.ml_lua_getallocf_rv(L, ud, rv) End
Function lua_getfenv:Void(L:lua_State, index:Int) MLI.ml_lua_getfenv(L, index) End
Function lua_getfield:Void(L:lua_State, index:Int, k:String) MLI.ml_lua_getfield(L, index, k) End
Function lua_getglobal:Void(L:lua_State, name:String) MLI.ml_lua_getglobal(L, name) End
' gethook
' gethookcount
' gethookmask
' getinfo
' getlocal
Function lua_getmetatable:Int(L:lua_State, index:Int) Return MLI.ml_lua_getmetatable(L, index) End
' getstack
Function lua_gettable:Void(L:lua_State, index:Int) MLI.ml_lua_gettable(L, index) End
Function lua_gettop:Int(L:lua_State) Return MLI.ml_lua_gettop(L) End
Function lua_getupvalue:String(L:lua_State, funcindex:Int, n:Int) Return MLI.ml_lua_getupvalue(L, funcindex, n) End
Function lua_insert:Void(L:lua_State, index:Int) MLI.ml_lua_insert(L, index) End
Function lua_isboolean:Int(L:lua_State, index:Int) Return MLI.ml_lua_isboolean(L, index) End
Function lua_iscfunction:Int(L:lua_State, index:Int) Return MLI.ml_lua_iscfunction(L, index) End
Function lua_isfunction:Int(L:lua_State, index:Int) Return MLI.ml_lua_isfunction(L, index) End
Function lua_islightuserdata:Int(L:lua_State, index:Int) Return MLI.ml_lua_islightuserdata(L, index) End
Function lua_isnil:Int(L:lua_State, index:Int) Return MLI.ml_lua_isnil(L, index) End
Function lua_isnone:Int(L:lua_State, index:Int) Return MLI.ml_lua_isnone(L, index) End
Function lua_isnoneornil:Int(L:lua_State, index:Int) Return MLI.ml_lua_isnoneornil(L, index) End
Function lua_isnumber:Int(L:lua_State, index:Int) Return MLI.ml_lua_isnumber(L, index) End
Function lua_isstring:Int(L:lua_State, index:Int) Return MLI.ml_lua_isstring(L, index) End
Function lua_istable:Int(L:lua_State, index:Int) Return MLI.ml_lua_istable(L, index) End
Function lua_isthread:Int(L:lua_State, index:Int) Return MLI.ml_lua_isthread(L, index) End
Function lua_isuserdata:Int(L:lua_State, index:Int) Return MLI.ml_lua_isuserdata(L, index) End
Function lua_lessthan:Int(L:lua_State, index1:Int, index2:Int) Return MLI.ml_lua_lessthan(L, index1, index2) End
' load
Function lua_newstate:lua_State(f:lua_Alloc, ud:Pointer=NULL_PTR) Return MLI.ml_lua_newstate(f, ud) End
Function lua_newstate:lua_State(f:lua_Alloc, ud:Pointer=NULL_PTR, rv:lua_State) Return MLI.ml_lua_newstate_rv(f, ud, rv) End
Function lua_newtable:Void(L:lua_State) MLI.ml_lua_newtable(L) End
Function lua_newthread:lua_State(L:lua_State) Return MLI.ml_lua_newthread(L) End
Function lua_newthread:lua_State(L:lua_State, rv:lua_State) Return MLI.ml_lua_newthread_rv(L, rv) End
Function lua_newuserdata:Pointer(L:lua_State, size:Int) Return MLI.ml_lua_newuserdata(L, size) End
Function lua_newuserdata:Pointer(L:lua_State, size:Int, rv:Pointer) Return MLI.ml_lua_newuserdata_rv(L, size, rv) End
Function lua_next:Int(L:lua_State, index:Int) Return MLI.ml_lua_next(L, index) End
Function lua_objlen:Int(L:lua_State, index:Int) Return MLI.ml_lua_objlen(L, index) End
Function lua_pcall:Int(L:lua_State, nargs:Int, nresults:Int, msgh:Int) Return MLI.ml_lua_pcall(L, nargs, nresults, msgh) End
Function lua_pop:Void(L:lua_State, n:Int) MLI.ml_lua_pop(L, n) End
Function lua_pushboolean:Void(L:lua_State, b:Int) MLI.ml_lua_pushboolean(L, b) End
' pushcclosure
' pushcfunction
' pushfstring
Function lua_pushinteger:Void(L:lua_State, n:Int) MLI.ml_lua_pushinteger(L, n) End
Function lua_pushlightuserdata:Void(L:lua_State, p:Pointer) MLI.ml_lua_pushlightuserdata(L, p) End
Function lua_pushliteral:String(L:lua_State, s:String) Return MLI.ml_lua_pushliteral(L, s) End
Function lua_pushlstring:Void(L:lua_State, s:String, len:Int) MLI.ml_lua_pushlstring(L, s, len) End
Function lua_pushnil:Void(L:lua_State) MLI.ml_lua_pushnil(L) End
Function lua_pushnumber:Void(L:lua_State, n:Float) MLI.ml_lua_pushnumber(L, n) End
Function lua_pushstring:Void(L:lua_State, s:String) MLI.ml_lua_pushstring(L, s) End
Function lua_pushthread:Int(L:lua_State) Return MLI.ml_lua_pushthread(L) End
Function lua_pushvalue:Void(L:lua_State, index:Int) MLI.ml_lua_pushvalue(L, index) End
' pushvfstring
Function lua_rawequal:Int(L:lua_State, index1:Int, index2:Int) Return MLI.ml_lua_rawequal(L, index1, index2) End
Function lua_rawget:Void(L:lua_State, index:Int) MLI.ml_lua_rawget(L, index) End
Function lua_rawgeti:Void(L:lua_State, index:Int, n:Int) MLI.ml_lua_rawgeti(L, index, n) End
Function lua_rawset:Void(L:lua_State, index:Int) MLI.ml_lua_rawset(L, index) End
Function lua_rawseti:Void(L:lua_State, index:Int, n:Int) MLI.ml_lua_rawseti(L, index, n) End
' register
Function lua_remove:Void(L:lua_State, index:Int) MLI.ml_lua_remove(L, index) End
Function lua_replace:Void(L:lua_State, index:Int) MLI.ml_lua_replace(L, index) End
Function lua_resume:Int(L:lua_State, narg:Int) Return MLI.ml_lua_resume(L, narg) End
Function lua_setallocf:Void(L:lua_State, f:lua_Alloc, ud:Pointer) MLI.ml_lua_setallocf(L, f, ud) End
Function lua_setfenv:Int(L:lua_State, index:Int) Return MLI.ml_lua_setfenv(L, index) End
Function lua_setfield:Void(L:lua_State, index:Int, k:String) MLI.ml_lua_setfield(L, index, k) End
Function lua_setglobal:Void(L:lua_State, name:String) MLI.ml_lua_setglobal(L, name) End
' sethook
' setlocal
Function lua_setmetatable:Void(L:lua_State, index:Int) MLI.ml_lua_setmetatable(L, index) End
Function lua_settable:Void(L:lua_State, index:Int) MLI.ml_lua_settable(L, index) End
Function lua_settop:Void(L:lua_State, n:Int) MLI.ml_lua_settop(L, n) End
Function lua_setupvalue:String(L:lua_State, funcindex:Int, n:Int) Return MLI.ml_lua_setupvalue(L, funcindex, n) End
Function lua_status:Int(L:lua_State) Return MLI.ml_lua_status(L) End
Function lua_toboolean:Int(L:lua_State, index:Int) Return MLI.ml_lua_toboolean(L, index) End
' tocfunction
Function lua_tointeger:Int(L:lua_State, index:Int) Return MLI.ml_lua_tointeger(L, index) End
' tolstring
Function lua_tonumber:Float(L:lua_State, index:Int) Return MLI.ml_lua_tonumber(L, index) End
Function lua_topointer:Pointer(L:lua_State, index:Int) Return MLI.ml_lua_topointer(L, index) End
Function lua_tostring:String(L:lua_State, index:Int) Return MLI.ml_lua_tostring(L, index) End
Function lua_tothread:lua_State(L:lua_State, index:Int) Return MLI.ml_lua_tothread(L, index) End
Function lua_tothread:lua_State(L:lua_State, index:Int, rv:lua_State) Return MLI.ml_lua_tothread_rv(L, index, rv) End
Function lua_type:Int(L:lua_State, index:Int) Return MLI.ml_lua_type(L, index) End
Function lua_typename:String(L:lua_State, tp:Int) Return MLI.ml_lua_typename(L, tp) End
Function lua_xmove:Void(stfrom:lua_State, stto:lua_State, n:Int) MLI.ml_lua_xmove(stfrom, stto, n) End
Function lua_yield:Int(L:lua_State, nresults:Int) Return MLI.ml_lua_yield(L, nresults) End

' luaL externs
Function luaL_addchar:Void(B:luaL_Buffer, c:Int) MLI.ml_luaL_addchar(B, c) End
Function luaL_addlstring:Void(B:luaL_Buffer, s:String, l:Int) MLI.ml_luaL_addlstring(B, s, l) End
Function luaL_addsize:Void(B:luaL_Buffer, n:Int) MLI.ml_luaL_addsize(B, n) End
Function luaL_addstring:Void(B:luaL_Buffer, s:String) MLI.ml_luaL_addstring(B, s) End
Function luaL_addvalue:Void(B:luaL_Buffer) MLI.ml_luaL_addvalue(B) End
Function luaL_argcheck:Void(L:lua_State, cond:Int, narg:Int, extramsg:String) MLI.ml_luaL_argcheck(L, cond, narg, extramsg) End
Function luaL_argerror:Int(L:lua_State, narg:Int, extramsg:String) Return MLI.ml_luaL_argerror(L, narg, extramsg) End
Function luaL_buffinit:Void(L:lua_State, B:luaL_Buffer) MLI.ml_luaL_buffinit(L, B) End
Function luaL_callmeta:Int(L:lua_State, obj:Int, e:String) Return MLI.ml_luaL_callmeta(L, obj, e) End
Function luaL_checkany:Void(L:lua_State, narg:Int) MLI.ml_luaL_checkany(L, narg) End
Function luaL_checkint:Int(L:lua_State, narg:Int) Return MLI.ml_luaL_checkint(L, narg) End
Function luaL_checkinteger:Int(L:lua_State, narg:Int) Return MLI.ml_luaL_checkinteger(L, narg) End
Function luaL_checklong:Int(L:lua_State, narg:Int) Return MLI.ml_luaL_checklong(L, narg) End
' checklstring
Function luaL_checknumber:Float(L:lua_State, narg:Int) Return MLI.ml_luaL_checknumber(L, narg) End
' checkoption
Function luaL_checkstack:Void(L:lua_State, sz:Int, msg:String) MLI.ml_luaL_checkstack(L, sz, msg) End
Function luaL_checkstring:String(L:lua_State, narg:Int) Return MLI.ml_luaL_checkstring(L, narg) End
Function luaL_checktype:Void(L:lua_State, narg:Int, t:Int) MLI.ml_luaL_checktype(L, narg, t) End
Function luaL_checkudata:Pointer(L:lua_State, narg:Int, tname:String) Return MLI.ml_luaL_checkudata(L, narg, tname) End
Function luaL_dofile:Int(L:lua_State, filename:String) Return MLI.ml_luaL_dofile(L, filename) End
Function luaL_dostring:Int(L:lua_State, str:String) Return MLI.ml_luaL_dostring(L, str) End
Function luaL_error:Int(L:lua_State, argcount%, fmt$, arg1$="", arg2$="", arg3$="", arg4$="", arg5$="", arg6$="", arg7$="", arg8$="", arg9$="", arg10$="")
	Return MLI.ml_luaL_error(L, argcount, fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10)
End
Function luaL_getmetafield:Int(L:lua_State, obj:Int, e:String) Return MLI.ml_luaL_getmetafield(L, obj, e) End
Function luaL_getmetatable:Void(L:lua_State, tname:String) MLI.ml_luaL_getmetatable(L, tname) End
Function luaL_gsub:String(L:lua_State, s:String, p:String, r:String) Return MLI.ml_luaL_gsub(L, s, p, r) End
Function luaL_loadbuffer:Int(L:lua_State, buff:Int[], sz:Int, name:String) Return MLI.ml_luaL_loadbuffer(L, buff, sz, name) End
Function luaL_loadfile:Int(L:lua_State, filename:String) Return MLI.ml_luaL_loadfile(L, filename) End
Function luaL_loadstring:Int(L:lua_State, s:String) Return MLI.ml_luaL_loadstring(L, s) End
Function luaL_newmetatable:Int(L:lua_State, tname:String) Return MLI.ml_luaL_newmetatable(L, tname) End
Function luaL_newstate:lua_State() Return MLI.ml_luaL_newstate() End
Function luaL_newstate:lua_State(rv:lua_State) Return MLI.ml_luaL_newstate_rv(rv) End
Function luaL_openlibs:Void(L:lua_State) MLI.ml_luaL_openlibs(L) End
Function luaL_optint:Int(L:lua_State, narg:Int, d:Int) Return MLI.ml_luaL_optint(L, narg, d) End
Function luaL_optinteger:Int(L:lua_State, narg:Int, d:Int) Return MLI.ml_luaL_optinteger(L, narg, d) End
Function luaL_optlong:Int(L:lua_State, narg:Int, d:Int) Return MLI.ml_luaL_optlong(L, narg, d) End
' optlstring
Function luaL_optnumber:Float(L:lua_State, narg:Int, d:Float) Return MLI.ml_luaL_optnumber(L, narg, d) End
Function luaL_optstring:String(L:lua_State, narg:Int, d:String) Return MLI.ml_luaL_optstring(L, narg, d) End
' prepbuffer
' pushresult
Function luaL_ref:Int(L:lua_State, t:Int) Return MLI.ml_luaL_ref(L, t) End
' register
Function luaL_typename:String(L:lua_State, index:Int) Return MLI.ml_luaL_typename(L, index) End
Function luaL_typerror:Int(L:lua_State, narg:Int, tname:String) Return MLI.ml_luaL_typerror(L, narg, tname) End
Function luaL_unref:Void(L:lua_State, t:Int, ref:Int) MLI.ml_luaL_unref(L, t, ref) End
Function luaL_where:Void(L:lua_State, lvl:Int) MLI.ml_luaL_where(L, lvl) End

''''''''''''''''''' Lua Constants '''''''''''''''''''

Const LUA_MULTRET:Int = -1

Const LUA_REGISTRYINDEX:Int = -10000
Const LUA_ENVIRONINDEX:Int = -10001
Const LUA_GLOBALSINDEX:Int = -10002

Const LUA_OK:Int = 0
Const LUA_YIELD:Int = 1
Const LUA_ERRRUN:Int = 2
Const LUA_ERRSYNTAX:Int = 3
Const LUA_ERRMEM:Int = 4
Const LUA_ERRERR:Int = 5

Const LUA_TNONE:Int = -1
Const LUA_TNIL:Int = 0
Const LUA_TBOOLEAN:Int = 1
Const LUA_TLIGHTUSERDATA:Int = 2
Const LUA_TNUMBER:Int = 3
Const LUA_TSTRING:Int = 4
Const LUA_TTABLE:Int = 5
Const LUA_TFUNCTION:Int = 6
Const LUA_TUSERDATA:Int = 7
Const LUA_TTHREAD:Int = 8

Const LUA_MINSTACK:Int = 20

Const LUA_GCSTOP:Int = 0
Const LUA_GCRESTART:Int = 1
Const LUA_GCCOLLECT:Int = 2
Const LUA_GCCOUNT:Int = 3
Const LUA_GCCOUNTB:Int = 4
Const LUA_GCSTEP:Int = 5
Const LUA_GCSETPAUSE:Int = 6
Const LUA_GCSETSTEPMUL:Int = 7

Const LUA_HOOKCALL:Int = 0
Const LUA_HOOKRET:Int = 1
Const LUA_HOOKLINE:Int = 2
Const LUA_HOOKCOUNT:Int = 3
Const LUA_HOOKTAILRET:Int = 4

Const LUA_MASKCALL:Int = 1 Shl LUA_HOOKCALL
Const LUA_MASKRET:Int = 1 Shl LUA_HOOKRET
Const LUA_MASKLINE:Int = 1 Shl LUA_HOOKLINE
Const LUA_MASKCOUNT:Int = 1 Shl LUA_HOOKCOUNT

Const LUA_COLIBNAME:String = "coroutine"
Const LUA_TABLIBNAME:String = "table"
Const LUA_IOLIBNAME:String = "io"
Const LUA_OSLIBNAME:String = "os"
Const LUA_STRLIBNAME:String = "string"
Const LUA_MATHLIBNAME:String = "math"
Const LUA_DBLIBNAME:String = "debug"
Const LUA_LOADLIBNAME:String = "package"

Const LUA_ERRFILE:Int = LUA_ERRERR + 1
Const LUA_NOREF:Int = -2
Const LUA_REFNIL:Int = -1

Const LAST_TAG:Int = LUA_TTHREAD
Const LUA_TPROTO:Int = LAST_TAG+1
Const LUA_TUPVAL:Int = LAST_TAG+2
Const LUA_TDEADKEY:Int = LAST_TAG+3

''''''''''''''''''' Public Classes '''''''''''''''''''

' User-friendly Lua state wrapper (lua_State is an externed struct* wrapper in C, and a externed class in Java)
Class LuaState Final
Private
	Global nextId:Int = 0
	Global states:IntMap<LuaState> = New IntMap<LuaState>
	
	Field state:lua_State
	Field id:Int
	
Public
' Public functions
	Function GetLuaState:LuaState(index:Int)
		Return states.Get(index)
	End
	Function GetLuaState:LuaState(state:lua_State)
		For Local key:Int = EachIn states.Keys()
			If states.Get(key).state.Equals(state) Then Return states.Get(key)
		Next
		Return Null
	End
	
' Readonly properties
	Method ID:Int() Property
		Return id
	End
	Method State:lua_State() Property
		Return state
	End
	
' Constructors
	Method New()
		state = luaL_newstate(New lua_State)
		luaL_openlibs(state) ' TODO: maybe remove this?
		id = nextId
		nextId+=1
		states.Set(id, Self)
		MLI.ml_Init(state)
	End
	
	Method New(existingState:lua_State)
		state = existingState
		id = nextId
		nextId+=1
		states.Set(id, Self)
	End
	
' Public methods
	Method Close:Void()
		If IsClosed() Then Return
		MLI.refCounter.ReleaseAll()
		lua_close(state)
		state = Null
		states.Remove(id)
	End
	
	Method IsClosed:Bool()
		Return state = Null
	End
	
	Method NewThread:LuaState()
		Return New LuaState(lua_newthread(state))
	End
	
' Stack methods
	Method GetTop:Int()
		Return lua_gettop(state)
	End
	
	Method SetTop:Void(index:Int)
		lua_settop(state, index)
	End
	
	Method PushValue:Void(index:Int)
		lua_pushvalue(state, index)
	End
	
	Method Remove:Void(index:Int)
		lua_remove(state, index)
	End
	
	Method Insert:Void(index:Int)
		lua_insert(state, index)
	End
	
	Method Replace:Void(index:Int)
		lua_replace(state, index)
	End
	
	Method CheckStack:Int(size:Int)
		Return lua_checkstack(state, size)
	End
	
	Method XMove:Void(toState:LuaState, n:Int)
		lua_xmove(state, toState.state, n)
	End
	
' Access methods
	Method IsNumber:Bool(index:Int)
		Return lua_isnumber(state, index) <> 0
	End
	
	Method IsString:Bool(index:Int)
		Return lua_isstring(state, index) <> 0
	End
	
	Method IsFunction:Bool(index:Int)
		Return lua_isfunction(state, index) <> 0
	End
	
	Method IsCFunction:Bool(index:Int)
		Return lua_iscfunction(state, index) <> 0
	End
	
	Method IsUserdata:Bool(index:Int)
		Return lua_isuserdata(state, index) <> 0
	End
	
	Method IsTable:Bool(index:Int)
		Return lua_istable(state, index) <> 0
	End
	
	Method IsBoolean:Bool(index:Int)
		Return lua_isboolean(state, index) <> 0
	End
	
	Method IsNil:Bool(index:Int)
		Return lua_isnil(state, index) <> 0
	End
	
	Method IsThread:Bool(index:Int)
		Return lua_isthread(state, index) <> 0
	End
	
	Method IsNone:Bool(index:Int)
		Return lua_isnone(state, index) <> 0
	End
	
	Method IsNoneOrNil:Bool(index:Int)
		Return lua_isnoneornil(state, index) <> 0
	End
	
	Method Type:Int(index:Int)
		Return lua_type(state, index)
	End
	
	Method TypeName:String(tp:Int)
		Return lua_typename(state, tp)
	End
	
	Method TypeError:String(narg:Int, tname:String)
		Return luaL_typerror(state, narg, tname)
	End
	
	Method RawEqual:Int(index1:Int, index2:Int)
		Return lua_rawequal(state, index1, index2)
	End
	
	Method ToNumber:Float(index:Int)
		Return lua_tonumber(state, index)
	End
	
	Method ToInteger:Int(index:Int)
		Return lua_tointeger(state, index)
	End
	
	Method ToBoolean:Bool(index:Int)
		Return lua_toboolean(state, index) <> 0
	End
	
	Method ToString:String(index:Int)
		Return lua_tostring(state, index)
	End
	
	Method ToThread:LuaState(index:Int)
		Local st:lua_State = lua_tothread(state, index)
		Local rv:LuaState = GetLuaState(st)
		If rv = Null Then rv = New LuaState(st)
		Return rv
	End
	
	Method ToPointer:Pointer(index:Int)
		Return lua_topointer(state, index)
	End
	
' Push methods
	Method PushNil:Void()
		lua_pushnil(state)
	End
	
	Method PushNumber:Void(n:Float)
		lua_pushnumber(state, n)
	End
	
	Method PushInteger:Void(n:Int)
		lua_pushinteger(state, n)
	End
	
	Method PushString:Void(s:String)
		lua_pushstring(state, s)
	End
	
	Method PushString:Void(bytes:Int[])
		lua_pushstring(state, String.FromChars(bytes))
	End
	
	Method PushBoolean:Void(b:Bool)
		lua_pushboolean(state, b)
	End
	
' Get methods
	Method GetTable:Void(index:Int)
		lua_gettable(state, index)
	End
	
	Method GetField:Void(index:Int, k:String)
		lua_getfield(state, index, k)
	End
	
	Method GetGlobal:Void(k:String)
		lua_getglobal(state, k)
	End
	
	Method RawGet:Void(index:Int)
		lua_rawget(state, index)
	End
	
	Method RawGetI:Void(index:Int, n:Int)
		lua_rawgeti(state, index, n)
	End
	
	Method CreateTable:Void(narr:Int, nrec:Int)
		lua_createtable(state, narr, nrec)
	End
	
	Method NewTable:Void()
		lua_newtable(state)
	End
	
	Method GetMetatable:Int(index:Int)
		Return lua_getmetatable(state, index)
	End
	
' Set methods
	Method SetTable:Void(index:Int)
		lua_settable(state, index)
	End
	
	Method SetField:Void(index:Int, k:String)
		lua_setfield(state, index, k)
	End
	
	Method SetGlobal:Void(k:String)
		lua_setglobal(state, k)
	End
	
	Method RawSet:Void(index:Int)
		lua_rawset(state, index)
	End
	
	Method RawSetI:Void(index:Int, n:Int)
		lua_rawseti(state, index, n)
	End
	
	Method SetMetatable:Void(index:Int)
		lua_setmetatable(state, index)
	End
	
	Method Call:Void(nargs:Int, nresults:Int)
		lua_call(state, nargs, nresults)
	End
	
	Method PCall:Int(nargs:Int, nresults:Int, errfunc:Int)
		Return lua_pcall(state, nargs, nresults, errfunc)
	End
	
	Method Yield:Int(nresults:Int)
		Return lua_yield(state, nresults)
	End
	
	Method Resume:Int(nargs:Int)
		Return lua_resume(state, nargs)
	End
	
	Method Status:Int()
		Return lua_status(state)
	End
	
	Method GC:Int(what:Int, data:Int)
		Return lua_gc(state, what, data)
	End
	
	Method NextField:Int(index:Int) ' Next is a reserved word
		Return lua_next(state, index)
	End
	
	Method Err:Int()
		Return lua_error(state)
	End
	
	Method Concat:Void(n:Int)
		lua_concat(state, n)
	End
	
	Method GetFEnv:Void(index:Int)
		lua_getfenv(state, index)
	End
	
	Method GetUpValue:String(funcindex:Int, n:Int)
		lua_getupvalue(state, funcindex, n)
	End

	Method LessThan:Bool(index1:Int, index2:Int)
		Return lua_lessthan(state, index1, index2) <> 0
	End
	
	Method ObjLen:Int(index:Int)
		Return lua_objlen(state, index)
	End
	
	Method SetFEnv:Int(index:Int)
		Return lua_setfenv(state, index)
	End
	
	Method SetUpValue:String(funcindex:Int, n:Int)
		Return lua_setupvalue(state, funcindex, n)
	End
	
' Auxlib
	Method DoFile:Int(filename:String)
		Return luaL_dofile(state, filename)
	End
	
	Method DoString:Int(str:String)
		Return luaL_dostring(state, str)
	End
	
	Method GetMetaField:Int(obj:Int, e:String)
		Return luaL_getmetafield(state, obj, e)
	End
	
	Method CallMeta:Int(obj:Int, e:String)
		Return luaL_callmeta(state, obj, e)
	End
	
	Method ArgError:Int(narg:Int, extramsg:String)
		Return luaL_argerror(state, narg, extramsg)
	End
	
	Method CheckString:String(narg:Int)
		Return luaL_checkstring(state, narg)
	End
	
	Method OptString:String(narg:Int, def:String)
		Return luaL_optstring(state, narg, def)
	End
	
	Method CheckNumber:Float(narg:Int)
		Return luaL_checknumber(state, narg)
	End
	
	Method OptNumber:Float(narg:Int, def:Float)
		Return luaL_optnumber(state, narg, def)
	End
	
	Method CheckInteger:Int(narg:Int)
		Return luaL_checkinteger(state, narg)
	End
	
	Method OptInteger:Int(narg:Int, def:Int)
		Return luaL_optinteger(state, narg, def)
	End
	
	Method CheckStack:Void(sz:Int, msg:String)
		luaL_checkstack(state, sz, msg)
	End
	
	Method CheckType:Void(narg:Int, t:Int)
		luaL_checktype(state, narg, t)
	End
	
	Method CheckAny:Void(narg:Int)
		luaL_checkany(state, narg)
	End
	
	Method CheckUData:Pointer(narg:Int, tname:String)
		Return luaL_checkudata(narg, tname)
	End
	
	Method NewMetatable:Int(tname:String)
		Return luaL_newmetatable(state, tname)
	End
	
	Method GetMetatable:Void(tname:String)
		luaL_getmetatable(state, tname)
	End
	
	Method Where:Void(lvl:Int)
		luaL_where(state, lvl)
	End
	
	Method Ref:Int(t:Int)
		Return luaL_ref(state, t)
	End
	
	Method Unref:Void(t:Int, ref:Int)
		luaL_unref(state, t, ref)
	End
	
	Method LoadFile:Int(filename:String)
		Return luaL_loadfile(state, filename)
	End
	
	Method LoadString:Int(s:String)
		Return luaL_loadstring(state, s)
	End
	
	Method LoadBuffer:Int(buff:Int[], name:String)
		Return luaL_loadbuffer(state, buff, buff.Length, name)
	End
	
	Method Pop:Void(n:Int)
		lua_pop(state, n)
	End
	
	' Horrible wrapper due to no varargs in monkey.
	' Only %s works, sorry! You can still pass in numbers, it will just see them as strings.
	Method FormatError:Void(argcount:Int, fmt$, arg1$="", arg2$="", arg3$="", arg4$="", arg5$="", arg6$="", arg7$="", arg8$="", arg9$="", arg10$="")
		luaL_error(state, argcount, fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10)
	End

' Monkey<->Lua proxy methods
	Method IsMonkeyObject:Bool(idx:Int)
		Return MLI.ml_IsMonkeyObject(state, idx)
	End
	
	Method IsMonkeyFunction:Bool(idx:Int)
		If MLI.ml_IsMonkeyObject(state, idx) Then
			Local o:Object = MLI.ml_ToMonkeyObject(state, idx)
			If ml_MonkeyFunction(o) Then Return True
		End
		Return False
	End
	
	Method PushMonkeyObject:Void(obj:Object)
		MLI.ml_PushMonkeyObject(state, obj)
	End
	
	Method PushMonkeyFunction:Void(func:MonkeyFunction)
		MLI.ml_PushMonkeyFunction(state, func)
	End
	
	Method ToMonkeyObject:Object(idx:Int)
		If IsNil(idx) Then Return Null
		If IsMonkeyObject(idx) Then Return MLI.ml_ToMonkeyObject(state, idx)
		If IsBoolean(idx) Then Return BoxBool(ToBoolean(idx))
		If IsNumber(idx) Then Return BoxFloat(ToNumber(idx))
		If IsString(idx) Then Return BoxString(ToString(idx))
		Return Null
	End
	
	Method ToMonkeyFunction:MonkeyFunction(idx:Int)
		If IsNil(idx) Then Return Null
		If IsMonkeyFunction(idx) Then Return MonkeyFunction(MLI.ml_ToMonkeyFunction(state, idx))
		Return Null
	End
End

' Wrapper on ml_MonkeyFunction that hides the lua_State class and has a self-registering method
Class MonkeyFunction Extends ml_MonkeyFunction Abstract
Private
	Method ml_Func:Int(state:lua_State)
		Local st:LuaState = LuaState.GetLuaState(state)
		Return Func(LuaState.GetLuaState(state))
	End
Public
	' Implement this to provide your callback code
	Method Func:Int(state:LuaState) Abstract
	
	' Registers this function as a global variable with the passed name
	Method Register:MonkeyFunction(state:LuaState, name:String)
		state.PushMonkeyFunction(Self)
		state.SetGlobal(name)
		Return Self
	End
End

Function RegisterGlobal:Void(state:LuaState, name:String, value:String)
	state.PushString(value)
	state.SetGlobal(name)
End

Function RegisterGlobal:Void(state:LuaState, name:String, value:Int)
	state.PushInteger(value)
	state.SetGlobal(name)
End

Function RegisterGlobal:Void(state:LuaState, name:String, value:Float)
	state.PushNumber(value)
	state.SetGlobal(name)
End

Function RegisterGlobalObject:Void(state:LuaState, name:String, value:Object)
	state.PushMonkeyObject(value)
	state.SetGlobal(name)
End

Function RegisterGlobalFunction:Void(state:LuaState, name:String, value:MonkeyFunction)
	state.PushMonkeyObject(value)
	state.SetGlobal(name)
End
